package trb.complexshield.benchmarking;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.LineArray;
import no.hrp.hvrc.rad.calc.intersect.Segment;
import no.hrp.hvrc.utils.j3d.BBox;
import no.hrp.hvrc.utils.j3d.Vec3;
import trb.complexshield.ComplexShield;
import trb.complexshield.ComplexShieldOutput;
import trb.complexshield.bool.SegmentOutput;

/**
 * adding memory: boolean 1.03518 1183000 79964.16
 * use memory in BoolOr: boolean 0.948178 1183000 79964.16
 * use memory in SubtractSegments: boolean 0.953702 1183000 79964.16
 * rewrote merge to take array: boolean 0.895196 1183000 79964.16
 */
public class SegmentsBenchmark {

    public static Object createBoolModel() {
        //return BenchmarkSphere.createBoolModel(new BenchmarkSphere(1, new Mat4().translate(0, 1, 0)));
        return BenchmarkTub.createBoolModel();
    }

    public static float[] createSourcePositions(Vec3 min, Vec3 max, int sourceCount) {
        Random random = new Random(123);
        float[] coords = new float[sourceCount * 3];
        for (int i=0; i< sourceCount; i++) {
            coords[i*3+0] = min.x + random.nextFloat() * (max.x - min.x);
            coords[i*3+1] = min.y + random.nextFloat() * (max.y - min.y);
            coords[i*3+2] = min.z + random.nextFloat() * (max.z - min.z);
        }
        return coords;
    }

    public static float[] createMeasurePositions(BBox bbox) {
        int size = 30;
        float[] coords = new float[size * size * size * 3];
        Vec3 lower = bbox.getLower();
        Vec3 upper = bbox.getUpper();
        int dstIdx = 0;
        for (int zIdx = 0; zIdx < size; zIdx++) {
            for (int yIdx = 0; yIdx < size; yIdx++) {
                for (int xIdx = 0; xIdx < size; xIdx++) {
                    coords[dstIdx++] = lower.x + xIdx / (size - 1f) * (upper.x - lower.x);
                    coords[dstIdx++] = lower.y + yIdx / (size - 1f) * (upper.y - lower.y);
                    coords[dstIdx++] = lower.z + zIdx / (size - 1f) * (upper.z - lower.z);
                }
            }
        }

        return coords;
    }

    final float[] measurePositions;
    final float[] sourcePositions;
    final Vec3 sourcePos = new Vec3();
    final Vec3 measurePos = new Vec3();
    final ComplexShieldOutput output = new ComplexShieldOutput();
    final Segment segment = new Segment(new Vec3(), new Vec3());

    public SegmentsBenchmark(float[] measurePositions, float[] sourcePositions) {
        this.measurePositions = measurePositions.clone();
        this.sourcePositions = sourcePositions.clone();
    }

    public GeometryArray createIntersectionVisualisation(ComplexShield complexShield) {
        List<SegmentOutput> segmentOutputList = new ArrayList();
        for (Segment s : createSegments(sourcePositions, measurePositions)) {
            ComplexShieldOutput out = new ComplexShieldOutput();
            complexShield.intersect(s, out);
            segmentOutputList.add(new SegmentOutput(s, out));
        }
        return createIntersectionVisualisation(segmentOutputList);
    }

    public GeometryArray createIntersectionVisualisation(List<SegmentOutput> segmentOutputList) {
        List<Vec3> vertexList = new ArrayList<>();
        for (SegmentOutput segmentOutput : segmentOutputList) {
            Segment s = segmentOutput.segment;
            ComplexShieldOutput out = segmentOutput.output;
            for (int i=0; i<out.size; i+=2) {
                vertexList.add(new Vec3().interpolate_(s.originRef(), s.targetRef(), out.data[i]));
                vertexList.add(new Vec3().interpolate_(s.originRef(), s.targetRef(), out.data[i+1]));
            }
        }
        float[] floats = Vec3.toPackedFloats(vertexList);
        LineArray lineArray = new LineArray(Math.max(2, vertexList.size()), LineArray.COORDINATES);
        lineArray.setCoordinates(0, floats);
        return lineArray;
    }

    public static List<Segment> createSegments(float[] sourcePositions, float[] measurePositions) {
        List<Segment> list = new ArrayList<Segment>();
        for (Vec3 sourceVec : toVec3List(sourcePositions)) {
            for (Vec3 measureVec : toVec3List(measurePositions)) {
                list.add(new Segment(sourceVec, measureVec));
            }
        }

        return list;
    }

    private static List<Vec3> toVec3List(float[] floats) {
        List<Vec3> list = new ArrayList<Vec3>();
        for (int off=0; off < floats.length; off+=3) {
            Vec3 sourcePos = new Vec3();
            sourcePos.x = floats[off];
            sourcePos.y = floats[off + 1];
            sourcePos.z = floats[off + 2];
            list.add(sourcePos);
        }
        return list;
    }
}
